﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Data.Schema.Sql.SchemaModel;
using Microsoft.VisualStudio.Data.Schema.Package.Project;
using Microsoft.Data.Schema.SchemaModel;
using System.IO;

namespace TFDP.Common
{
    public static class ModelExtensions
    {
        /// <summary>
        /// Check if the element specified by name and of type T exists in the model
        /// </summary>
        /// <typeparam name="T">The type of element to find</typeparam>
        /// <param name="model">The ModelStore on which to operate</param>
        /// <param name="name">The fully qualified name of the element</param>
        /// <returns>true if the element exists, otherwise false</returns>
        public static bool Has<T>(this ModelStore model, string name) where T : class, IModelElement
        {
            ModelIdentifier identifier = model.CreateIdentifier(name.Split('.'));

            IList<T> matches = model.GetElements<T>(identifier, ModelElementQueryFilter.Internal);

            return (matches != null && matches.Count == 1);
        }

        /// <summary>
        /// Obtain the element specified by name and of type T from the model
        /// </summary>
        /// <typeparam name="T">The type of element to obtain</typeparam>
        /// <param name="model">The ModelStore on which to operate</param>
        /// <param name="name">The fully qualified name of the element</param>
        /// <returns>The specified element, null if the element does not exist in the model</returns>
        public static T Get<T>(this ModelStore model, string name) where T : class, IModelElement
        {
            ModelIdentifier identifier = model.CreateIdentifier(name.Split('.'));

            IList<T> matches = model.GetElements<T>(identifier, ModelElementQueryFilter.Internal);

            if (matches != null && matches.Count == 1)
                return matches[0];
            else
                return default(T);
        }

        /// <summary>
        /// Obtain the elements from the model that are of the specified type and
        /// which short name contains the specified name
        /// </summary>
        /// <typeparam name="T">The type of element to obtain</typeparam>
        /// <param name="model">The ModelStore on which to operate</param>
        /// <param name="name">The pattern to match the elements</param>
        /// <returns>The elements that match the criteria, otherwise an empty IEnumerable&lt;T&gt;</returns>
        public static IEnumerable<T> GetMany<T>(this ModelStore model, string name) where T : class, IModelElement
        {
            IList<T> elements = model.GetElements<T>(ModelElementQueryFilter.Internal);

            var matches = from e in elements
                          where e.GetName().Contains(name)
                          select e;

            if (matches.Count() == 0)
                return new List<T>();
            else
                return matches;
        }

        /// <summary>
        /// Check if the shortname of the current instance matches the specified name
        /// </summary>
        /// <param name="element">The instance element</param>
        /// <param name="name">The name to match</param>
        /// <returns>true if the shortname matches, otherwise false</returns>
        public static bool IsNameEqual(this IModelElement element, string name)
        {
            return String.Compare(element.GetName(), name, true) == 0;
        }

        /// <summary>
        /// Check if the shortname of the current instance matches the name of the specified element
        /// </summary>
        /// <param name="element">The instance element</param>
        /// <param name="other">The element to match</param>
        /// <returns>true if the shortname matches, otherwise false</returns>
        public static bool IsNameEqual(this IModelElement element, IModelElement other)
        {
            return String.Compare(element.GetName(), other.GetName(), true) == 0;
        }

        /// <summary>
        /// Check if the fully qualified name of the current instance matches the specified fully qualified name
        /// </summary>
        /// <param name="element">The instance element</param>
        /// <param name="name">The fully qualified name to match</param>
        /// <returns>true if the shortname matches, otherwise false</returns>
        public static bool IsFullNameEqual(this IModelElement element, string fullyQualifiedName)
        {
            return String.Compare(element.GetFullName(), fullyQualifiedName, true) == 0;
        }

        /// <summary>
        /// Check if the fully qualified name of the current instance matches the name of the specified element
        /// </summary>
        /// <param name="element">The instance element</param>
        /// <param name="other">The element to match</param>
        /// <returns>true if the fully qualified name matches, otherwise false</returns>
        public static bool IsFullNameEqual(this IModelElement element, IModelElement other)
        {
            return element.Name.Equals(other.Name);
        }

        public static T GetExtendedProperty<T>(this IModelElement element, string propertyName)
        {
            if (!(element is ISqlExtendedPropertyHost))
                return default(T);

            var propHost = (ISqlExtendedPropertyHost)element;
            
            var match = from p in propHost.ExtendedProperties
                        where p.Name.Parts.Last() == propertyName
                        select p;

            if (match.Count() == 0)
                return default(T);
            else
            {
                string value = match.First().Value.Script.Replace("'", String.Empty);
                if (typeof(T) == typeof(string))
                {
                    return (T)(object)value;
                }
                else
                    return default(T);
            }
        }

        public static string GetSchemaName(this IModelElement element)
        {
            if (element == null)
                throw new ArgumentNullException("element");

            if (element.Name == null)
                return String.Empty;

            return element.Name.Parts.First();
        }

        public static string GetName(this IModelElement element)
        {
            if (element == null)
                throw new ArgumentNullException("element");

            if (element.Name == null)
                return String.Empty;

            return element.Name.Parts.Last();
        }

        public static string GetFullName(this IModelElement element)
        {
            if (element == null)
                throw new ArgumentNullException("element");

            if (element.Name == null)
                return String.Empty;

            return String.Join(".", element.Name.Parts);
        }
    }
}
